using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;


[Serializable]
[Microsoft.SqlServer.Server.SqlUserDefinedAggregate(Format.UserDefined, MaxByteSize=1024)]
public struct VectorCov : IBinarySerialize
{
    private double[][] cov;
    private double[] avg;
    private int count;

    public void Init()
    {
        cov = null;
        avg = null;
        count = 0;
    }

    public void Accumulate(SqlBinary Value)
    {
        double[] vec = Elte.SqlSpIdx.Converters.VectorConverter.ToArray(Value);
        
        if (count == 0)
        {
            cov = new double[vec.Length][];
            for (int i = 0; i < cov.Length; i++)
                cov[i] = new double[vec.Length];
            avg = new double[vec.Length];
        }

        for (int i = 0; i < vec.Length; i++)
        {
            for (int j = 0; j < vec.Length; j++)
            {
                cov[i][j] += vec[i] * vec[j];
            }
            avg[i] += vec[i];
        }

        count++;
    }

    public void Merge(VectorCov Group)
    {
        if (count == 0)
        {
            cov = new double[Group.cov.Length][];
            for (int i = 0; i < cov.Length; i++)
                cov[i] = new double[cov.Length];
            avg = new double[Group.avg.Length];
        }

        for (int i = 0; i < cov.Length; i++)
        {
            for (int j = 0; j < cov[i].Length; j++)
            {
                cov[i][j] += Group.cov[i][j];
            }
            avg[i] += Group.avg[i];
        }

        count += Group.count;
    }

    public SqlBinary Terminate()
    {
        for (int k = 0; k < avg.Length; k++)
            avg[k] /= count;

        for (int i = 0; i < cov.Length; i++)
            for (int j = 0; j < cov[i].Length; j++)
            {
                cov[i][j] /= (count - 1);           // t
                cov[i][j] -= avg[i] * avg[j];       // t
            }

        return new SqlBinary(Elte.SqlSpIdx.Converters.MatrixConverter.ToBinary(cov));
    }

    #region IBinarySerialize Members

    public void Read(System.IO.BinaryReader r)
    {
        count = r.ReadInt32();
        int length = r.ReadInt32();
        avg = Elte.SqlSpIdx.Converters.VectorConverter.ToArray(r.ReadBytes(length * sizeof(double) + sizeof(int) + sizeof(int)));
        cov = Elte.SqlSpIdx.Converters.MatrixConverter.ToArray(r.ReadBytes(length * length * sizeof(double) + sizeof(int) + sizeof(int)));
    }

    public void Write(System.IO.BinaryWriter w)
    {
        w.Write(count);
        w.Write(avg.Length);
        w.Write(Elte.SqlSpIdx.Converters.VectorConverter.ToBinary(avg));
        w.Write(Elte.SqlSpIdx.Converters.MatrixConverter.ToBinary(cov));
    }

    #endregion
}
